/*
 *    Copyright (C) 2010 Renesas Electronics America Inc. All rights reserved.
 */
 
#include <tinyhal.h>
#include "..\..\FM4.h"
#include "Serial_Flash_BL.h"

#define LCD_DEBUG_FLASH_Init
//#define LCD_DEBUG_FLASH_Write
#define LCD_DEBUG_FLASH_EraseBlock

//--//

extern struct MEMORY_MAPPED_SERIAL_BLOCK_CONFIG g_SerialFlash_BL_Config;

extern FILESYSTEM_DRIVER_INTERFACE g_FAT32_FILE_SYSTEM_DriverInterface;
extern STREAM_DRIVER_INTERFACE g_FAT32_STREAM_DriverInterface;
extern struct BlockStorageDevice g_SerialFlash_Device;

extern FileSystemVolume g_SerialFlash_FS;

static BYTE wbuf[SF_PAGE_SIZE];
static BYTE rbuf[SF_PAGE_SIZE];

//--//
#define SF_READ_ID          0x9F
#define SF_CHIP_ERASE       0xc7
#define SF_SECTOR_ERASE     0xd8
#define SF_BYTE_PROGRAM     0x02
#define SF_BYTE_READ        0x03
#define SF_WRITE_ENABLE     0x06
#define SF_WRITE_DISABLE    0x04
#define SF_READ_STATUS      0x05
#define SF_WRITE_STATUS     0x01
#define UNPROTECT_STATUS    0x00
#define PROTECT_STATUS      0x3C
#define SF_DUAL_READ        0x0B

/*
 *  Register Bit Definition
 */
#define SMR_SOE          0x01U
#define SMR_SCKE         0x02U
#define SMR_BDS          0x04U
#define SMR_SCINV        0x08U
#define SMR_WUCR         0x10U
#define SMR_MD_UART      0x00U
#define SMR_MD_UART_MP   0x20U
#define SMR_MD_SIO       0x40U
#define SMR_MD_LIN       0x60U
#define SMR_MD_I2C       0x80U

#define SCR_TXE          0x01U
#define SCR_RXE          0x02U
#define SCR_TBIE         0x04U
#define SCR_TIE          0x08U
#define SCR_RIE          0x10U
#define SCR_SPI          0x20U
#define SCR_MS           0x40U
#define SCR_UPCL         0x80U

#define SSR_TBI          0x01U
#define SSR_TDRE         0x02U
#define SSR_RDRF         0x04U
#define SSR_ORE          0x08U
#define SSR_FRE          0x10U
#define SSR_PE           0x20U
#define SSR_REC          0x80U

#define ESCR_P           0x08U
#define ESCR_PEN         0x10U
#define ESCR_INV         0x20U
#define ESCR_ESBL        0x40U
#define ESCR_FLWEN       0x80U
#define ESCR_DATABITS_8  0x00U
#define ESCR_DATABITS_5  0x01U
#define ESCR_DATABITS_6  0x02U
#define ESCR_DATABITS_7  0x03U
#define ESCR_DATABITS_9  0x04U

#define BGR_EXT          0x8000U

#define FCR1_FSEL        0x01U
#define FCR1_FTIE        0x02U
#define FCR1_FDRQ        0x04U
#define FCR1_FRIIE       0x08U
#define FCR1_FLSTE       0x10U

#define FCR0_FE1         0x01U
#define FCR0_FE2         0x02U
#define FCR0_FCL1        0x04U
#define FCR0_FCL2        0x08U
#define FCR0_FSET        0x10U
#define FCR0_FLD         0x20U
#define FCR0_FLST        0x40U

#define DEF_BAUDRATE    1000    // 1MHz, unit=KHz

#ifdef MB9BF618T
FM4_MFS03_CSIO_TypeDef *spi = FM4_MFS1_CSIO;
#endif
#ifdef MB9BF506N
FM4_MFS47_CSIO_TypeDef *spi = FM4_MFS4_CSIO;
#endif

void RSPI_Initialize()
{
#ifdef MB9BF618T
    // channel 1-2
    FM4_GPIO->PFRF |= 0x0007;       // SIN1_2(PF0), SOT1_2(PF1), SCK1_2(PF2)
    FM4_GPIO->EPFR07 |= 0x0000FC00; // SIN1_2(PF0), SOT1_2(PF1), SCK1_2(PF2)
#endif
#ifdef MB9BF506N
    // channel 4-2
    FM4_GPIO->PFR0 |= 0x00E0;       // SIN4_2, SOT4_2, SCK4_2
    FM4_GPIO->EPFR08 |= 0x000003F0; // SIN4_2, SOT4_2, SCK4_2
#endif
    spi->SCR = SCR_UPCL;        // Initialize
    spi->SMR |= (SMR_MD_SIO     // SPI mode
                | SMR_SCINV     // Down edge(transfer)/Up edge(receive)
                | SMR_BDS       // MBS first
                | SMR_SCKE      // CLK enable
                | SMR_SOE);     // Data output enable
    uint16_t bgr = (uint16_t)((PCLK2 / (DEF_BAUDRATE * 1000)) - 1);
    spi->BGR = bgr;
    spi->ESCR = (ESCR_DATABITS_8);  // 8 bit
    spi->SCR |= (SCR_SPI |      // SPI mode
                SCR_RXE |       // Receive enable
                SCR_TXE);       // Transfer enable
}

void spi_wt (uint8_t dat)
{
    while ((spi->SSR & SSR_TDRE) == 0);
    spi->TDR = dat;
    while ((spi->SSR & SSR_RDRF) == 0);
    spi->RDR;
}

uint8_t spi_rd(void)
{
    while ((spi->SSR & SSR_TDRE) == 0);
    spi->TDR = 0xff;
    while ((spi->SSR & SSR_RDRF) == 0);
    return (uint8_t)(spi->RDR);
}

void SF_CS_Init()
{
#ifdef MB9BF618T
    FM4_GPIO->PFR3 &= 0xDFFF;   // P3D GPIO
    FM4_GPIO->DDR3 |= 0x2000;   // P3D output
#endif
#ifdef MB9BF506N
    FM4_GPIO->PFR4 &= 0xFFFE;   // A22 P40 GPIO
    FM4_GPIO->DDR4 |= 0x0001;   // A22 P40 output
#endif
}

void SF_CS_On()
{
#ifdef MB9BF618T
    FM4_GPIO->PDOR3_f.PD = 0;
#endif
#ifdef MB9BF506N
    FM4_GPIO->PDOR4_f.P0 = 0;
#endif
}

void SF_CS_Off()
{
#ifdef MB9BF618T
    FM4_GPIO->PDOR3_f.PD = 1;
#endif
#ifdef MB9BF506N
    FM4_GPIO->PDOR4_f.P0 = 1;
#endif
}

void SpiTx(uint8_t data)
{
    spi_wt(data);
}

uint8_t SpiRx(void){
    return (spi_rd());
}

void CmdAndTx(uint8_t *cmd, uint32_t cmdSize, uint8_t *buf, uint32_t bufSize)
{
    SF_CS_On();
    while (cmdSize-- > 0)
        SpiTx(*cmd++);
    while (bufSize-- > 0)
        SpiTx(*buf++);
    SF_CS_Off();
}

void CmdAndRx(uint8_t *cmd, uint32_t cmdSize, uint8_t *buf, uint32_t bufSize)
{
    SF_CS_On();
    while (cmdSize-- > 0)
        SpiTx(*cmd++);
    while (bufSize-- > 0)
        *buf++ = SpiRx();
    SF_CS_Off();
}


uint8_t SF_ReadStatus()
{
    uint8_t status;
    uint8_t cmd = SF_READ_STATUS;
    CmdAndRx(&cmd, 1, (uint8_t *)&status, 1);
    return status;
}

void SF_Wait()
{
    while ((SF_ReadStatus() & 0x01) != 0) {
    }
}

void SF_WriteEnable()
{
    uint8_t cmd = SF_WRITE_ENABLE;
    CmdAndTx(&cmd, 1, (uint8_t *)NULL, 0);
}

void SF_WriteDisable()
{
    uint8_t cmd = SF_WRITE_DISABLE;
    CmdAndTx(&cmd, 1, (uint8_t *)NULL, 0);
}

void SF_WriteStatus(uint8_t status)
{
    uint8_t cmd = SF_WRITE_STATUS;
    SF_WriteEnable();
    CmdAndTx(&cmd, 1, (uint8_t *)&status, 1);
    SF_Wait();
    SF_WriteDisable();
}

void SF_Protect()
{
    SF_WriteStatus(PROTECT_STATUS);
}

void SF_Unprotect()
{
    SF_WriteStatus(UNPROTECT_STATUS);
}

void SF_ReadID(uint8_t *id)
{
    uint8_t cmd = SF_READ_ID;
    CmdAndRx(&cmd, 1, id, 3);
}

void SF_EraseSector(uint32_t addr)
{
    uint8_t cmd[4];
    cmd[0] = SF_SECTOR_ERASE;
    cmd[1] = (uint8_t)((addr >> 16) & 0xff);
    cmd[2] = (uint8_t)((addr >>  8) & 0xff);
    cmd[3] = (uint8_t)( addr        & 0xff);
    SF_WriteEnable();
    CmdAndTx((uint8_t *)&cmd, 4, (uint8_t *)NULL, 0);
    SF_Wait();
    SF_WriteDisable();
}

void SF_WriteBytes(uint32_t addr, uint8_t *buf, uint32_t bufSize)
{
    uint8_t cmd[4];
    cmd[0] = SF_BYTE_PROGRAM;
    cmd[1] = (uint8_t)((addr >> 16) & 0xff);
    cmd[2] = (uint8_t)((addr >>  8) & 0xff);
    cmd[3] = (uint8_t)( addr        & 0xff);
    SF_WriteEnable();
    CmdAndTx((uint8_t *)&cmd, 4, (uint8_t *)buf, bufSize);
    SF_Wait();
    SF_WriteDisable();
}

void SF_ReadBytes(uint32_t addr, uint8_t *buf, uint32_t bufSize)
{
    uint8_t cmd[4];
    cmd[0] = SF_BYTE_READ;
    cmd[1] = (uint8_t)((addr >> 16) & 0xff);
    cmd[2] = (uint8_t)((addr >>  8) & 0xff);
    cmd[3] = (uint8_t)( addr        & 0xff);
    CmdAndRx((uint8_t *)&cmd, 4, (uint8_t *)buf, bufSize);
}

//--//

BOOL SF_BS_Driver::InitializeDevice(void *context)
{
    BLOCK_CONFIG *config = (BLOCK_CONFIG*)context;
    unsigned char id[10];
    id[0] = id[1] = id[2] = 0;
    if (!config || !config->BlockDeviceInformation)
        return FALSE;
    RSPI_Initialize();
    SF_CS_Init();
    SF_WriteStatus(UNPROTECT_STATUS);
    SF_ReadID(id);
#ifdef LCD_DEBUG_FLASH_Init
    lcd_printf("SPI Flash:%02x%02x%02x ", id[0], id[1], id[2]);
#endif
    if ((id[0] == 0x20) && (id[1] == 0x20)) {
#ifdef LCD_DEBUG_FLASH_Init
        lcd_printf("OK\r\n");
#endif
        return TRUE;
    } else {
#ifdef LCD_DEBUG_FLASH_Init
        lcd_printf("NG\r\n");
#endif
        return FALSE;
    }
}

BOOL SF_BS_Driver::UnInitializeDevice(void *context)
{    
    return TRUE;
}

const BlockDeviceInfo *SF_BS_Driver::GetDeviceInfo(void *context)
{
    BLOCK_CONFIG *config = (BLOCK_CONFIG*)context;

    return config->BlockDeviceInformation;
}

#if 0
BOOL SF_BS_Driver::ReadSector(SectorAddress sectorAddress, UINT32 Offset, UINT32 NumBytes, BYTE* pSectorBuff, UINT32 BytesPerSector)
{
    NumBytes = (NumBytes + Offset > BytesPerSector ? BytesPerSector - Offset : NumBytes);

    sectorAddress *= BytesPerSector;
        
    SF_Byte_Read(sectorAddress, wbuf, BytesPerSector);
        
    memcpy(pSectorBuff, wbuf + Offset, NumBytes);
        
    return TRUE;
}
#endif

BOOL SF_BS_Driver::Read(void *context, ByteAddress phyAddress, UINT32 NumBytes, BYTE *pSectorBuff)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
    UINT32 RangeIndex;
    UINT32 RegionIndex;
    UINT32 rsize;
    debug_printf("Read(addr=%x, num=%x, psec=%x)\r\n", phyAddress, NumBytes, pSectorBuff);
    MEMORY_MAPPED_SERIAL_BLOCK_CONFIG* pConfig = (MEMORY_MAPPED_SERIAL_BLOCK_CONFIG*)context;
    const BlockDeviceInfo *deviceInfo = pConfig->BlockConfig.BlockDeviceInformation;
    if (deviceInfo->FindRegionFromAddress(phyAddress, RegionIndex, RangeIndex)) {
        phyAddress = (ByteAddress)((UINT32)phyAddress - g_SerialFlash_BL_Config.Memory.BaseAddress);
        while (NumBytes > 0) {
            if (NumBytes > SF_PAGE_SIZE)
                rsize = SF_PAGE_SIZE;
            else
                rsize = NumBytes;
            SF_ReadBytes((UINT32)phyAddress, pSectorBuff, rsize);
            NumBytes -= rsize;
            pSectorBuff += rsize;
            phyAddress += rsize;
        }
        return TRUE;
    } else {
        return FALSE;
    }
}

BOOL SF_BS_Driver::Write(void *context, ByteAddress Address, UINT32 NumBytes, BYTE *pSectorBuff, BOOL ReadModifyWrite )
{
    NATIVE_PROFILE_PAL_FLASH();
//    debug_printf("Write(addr=%x, num=%x, psec=%x)\r\n", Address, NumBytes, pSectorBuff);
     return WriteX(context, Address, NumBytes, pSectorBuff, FALSE);
}

BOOL SF_BS_Driver::Memset(void *context, ByteAddress phyAddr, UINT8 Data, UINT32 NumBytes)
{
    NATIVE_PROFILE_PAL_FLASH();
    return WriteX(context, phyAddr, NumBytes, &Data, TRUE);
}

BOOL SF_BS_Driver::WriteX(void *context, ByteAddress phyAddress, UINT32 NumBytes, BYTE *pSectorBuff, BOOL fSet)
{
    NATIVE_PROFILE_PAL_FLASH();
#ifdef LCD_DEBUG_FLASH_Write
    lcd_printf("Write(%x) %x", phyAddress, NumBytes);
#endif
    BOOL ret = TRUE;
    UINT32 RangeIndex;
    UINT32 RegionIndex;
    UINT32 addr, num, wsize;
    BYTE *psec;

    MEMORY_MAPPED_SERIAL_BLOCK_CONFIG* pConfig = (MEMORY_MAPPED_SERIAL_BLOCK_CONFIG*)context;
    const BlockDeviceInfo *deviceInfo = pConfig->BlockConfig.BlockDeviceInformation;
    if (fSet)
        memset((void *)&wbuf[0], (int)*pSectorBuff, SF_PAGE_SIZE);
    if (deviceInfo->FindRegionFromAddress(phyAddress, RegionIndex, RangeIndex)) {
        phyAddress = (ByteAddress)((UINT32)phyAddress - g_SerialFlash_BL_Config.Memory.BaseAddress);
        addr = (UINT32)phyAddress;
        num = NumBytes;
        psec = pSectorBuff;
        SF_Unprotect();
        while (num > 0) {
            if (num > SF_PAGE_SIZE)
                wsize = SF_PAGE_SIZE;
            else
                wsize = num;
            if (fSet) {
                SF_WriteBytes((UINT32)addr, (unsigned char *)&wbuf[0], wsize);
            } else {
                SF_WriteBytes((UINT32)addr, (unsigned char *)psec, wsize);
            }
            addr += wsize;
            num -= wsize;
            psec += wsize;
        }
        SF_Protect();
        addr = (UINT32)phyAddress;
        num = NumBytes;
        psec = pSectorBuff;
        while (num > 0) {
            if (num > SF_PAGE_SIZE)
                wsize = SF_PAGE_SIZE;
            else
                wsize = num;
            if (fSet) {
                SF_ReadBytes((UINT32)addr, (unsigned char *)&rbuf[0], wsize);
                if (memcmp((void *)&wbuf[0], (void *)&rbuf[0], wsize) != 0) {
                    ret = FALSE;
                    break;
                }
            } else {
                SF_ReadBytes((UINT32)addr, (unsigned char *)&rbuf[0], wsize);
                if (memcmp((void *)psec, (void *)&rbuf[0], wsize) != 0) {
                    ret = FALSE;
                    break;
                }
            }
            addr += wsize;
            num -= wsize;
            psec += wsize;
        }
    } else {
        ret = FALSE;
    }
#ifdef LCD_DEBUG_FLASH_Write
    lcd_printf(":%d\r\n", ret);
#endif
    return ret;
}

BOOL SF_BS_Driver::GetSectorMetadata(void* context, ByteAddress SectorStart, SectorMetadata* pSectorMetadata)
{
    return TRUE;
}

BOOL SF_BS_Driver::SetSectorMetadata(void* context, ByteAddress SectorStart, SectorMetadata* pSectorMetadata)
{
    return TRUE;
}

BOOL SF_BS_Driver::IsBlockErased(void *context, ByteAddress phyAddress, UINT32 NumBytes)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
    UINT32 RangeIndex;
    UINT32 RegionIndex;
    UINT32 rsize, i;
    MEMORY_MAPPED_SERIAL_BLOCK_CONFIG* pConfig = (MEMORY_MAPPED_SERIAL_BLOCK_CONFIG*)context;
    const BlockDeviceInfo *deviceInfo = pConfig->BlockConfig.BlockDeviceInformation;
    if (deviceInfo->FindRegionFromAddress(phyAddress, RegionIndex, RangeIndex)) {
        phyAddress = (ByteAddress)((UINT32)phyAddress - g_SerialFlash_BL_Config.Memory.BaseAddress);
        while (NumBytes > 0) {
            if (NumBytes > SF_PAGE_SIZE)
                rsize = SF_PAGE_SIZE;
            else
                rsize = NumBytes;
            SF_ReadBytes((UINT32)phyAddress, (unsigned char *)&rbuf[0], rsize);
            NumBytes -= rsize;
            phyAddress += rsize;
            for (i = 0; i < rsize; i++)
                if (rbuf[i] != 0xff) {
                    return FALSE;
                }
        }
        return TRUE;
    } else {
        return FALSE;
    }
}


BOOL SF_BS_Driver::EraseBlock(void *context, ByteAddress phyAddress)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
//#ifdef LCD_DEBUG_FLASH_EraseBlock
//    lcd_printf("Er: 0x%08x\r", phyAddr);
//#endif
#ifdef LCD_DEBUG_FLASH_EraseBlock
    lcd_printf("EraseBlock(%x):", phyAddress);
#endif
    GLOBAL_LOCK(FlashIrq);
    UINT32 iRegion, iRange, BytesPerSector, i, j, k;
    MEMORY_MAPPED_SERIAL_BLOCK_CONFIG* config = (MEMORY_MAPPED_SERIAL_BLOCK_CONFIG*)context;
    const BlockDeviceInfo *    deviceInfo = config->BlockConfig.BlockDeviceInformation;
    if (deviceInfo->Attribute.WriteProtected)
        return FALSE;
    if (deviceInfo->FindRegionFromAddress(phyAddress, iRegion, iRange)) {
        phyAddress = (ByteAddress)((UINT32)phyAddress - g_SerialFlash_BL_Config.Memory.BaseAddress);
        SF_Unprotect();
        SF_EraseSector((UINT32)phyAddress);
        SF_Protect();
#ifdef LCD_DEBUG_FLASH_EraseBlock
        lcd_printf("OK\r\n");
#endif
        return TRUE;
    } else
#ifdef LCD_DEBUG_FLASH_EraseBlock
        lcd_printf("NG\r\n");
#endif
        return FALSE;
}

void SF_BS_Driver::SetPowerState(void *context, UINT32 State)
{
    // our flash driver is always Power ON
    return ;
}

UINT32 SF_BS_Driver::MaxSectorWrite_uSec(void *context)
{
    NATIVE_PROFILE_PAL_FLASH();

    BLOCK_CONFIG *config = (BLOCK_CONFIG*)context;

    return config->BlockDeviceInformation->MaxSectorWrite_uSec;
}

UINT32 SF_BS_Driver::MaxBlockErase_uSec(void *context)
{
    NATIVE_PROFILE_PAL_FLASH();

    BLOCK_CONFIG *config = (BLOCK_CONFIG*)context;

    return config->BlockDeviceInformation->MaxBlockErase_uSec;
}

struct IBlockStorageDevice g_SerialFlash_BL_DeviceTable = 
{                          
    &SF_BS_Driver::InitializeDevice,
    &SF_BS_Driver::UnInitializeDevice,
    &SF_BS_Driver::GetDeviceInfo,
    &SF_BS_Driver::Read,
    &SF_BS_Driver::Write,
    &SF_BS_Driver::Memset,
    &SF_BS_Driver::GetSectorMetadata,
    &SF_BS_Driver::SetSectorMetadata,
    &SF_BS_Driver::IsBlockErased,
    &SF_BS_Driver::EraseBlock,
    &SF_BS_Driver::SetPowerState,
    &SF_BS_Driver::MaxSectorWrite_uSec,
    &SF_BS_Driver::MaxBlockErase_uSec,
};
